package com.jeesite.modules.billnopattern.service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.TemplateUtil;
import com.beust.jcommander.ParameterException;
import com.jeesite.common.idgen.IdGen;
import com.jeesite.common.kit.Kv;
import com.jeesite.common.kit.Okv;
import com.jeesite.common.utils.ValidationUtils;
import com.jeesite.modules.billnopattern.enums.BillPatternEnum;
import com.jeesite.modules.billnorecord.entity.BillNoRecord;
import com.jeesite.modules.billnorecord.service.BillNoRecordService;
import com.jeesite.modules.codingrule.entity.CodingRuleHeader;
import com.jeesite.modules.codingrule.entity.CodingRuleLines;
import com.jeesite.modules.codingrule.enums.BarcodeTypeEnum;
import com.jeesite.modules.codingrule.enums.CodingLineTypeEnum;
import com.jeesite.modules.codingrule.enums.CodingTypeEnum;
import com.jeesite.modules.codingrule.service.CodingRuleHeaderService;
import com.jeesite.modules.form.service.FormService;
import com.jeesite.modules.sys.entity.DictData;
import com.jeesite.modules.sys.service.DictDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.jeesite.common.entity.Page;
import com.jeesite.common.service.CrudService;
import com.jeesite.modules.billnopattern.entity.BillNoPattern;
import com.jeesite.modules.billnopattern.dao.BillNoPatternDao;

/**
 * 单据编码记录表Service
 *
 * @author 码脑洞
 * @version 2025-04-13
 */
@Service
public class BillNoPatternService extends CrudService<BillNoPatternDao, BillNoPattern> {

	@Autowired
	private FormService formService;
	@Autowired
	private CodingRuleHeaderService codingRuleHeaderService;
	@Autowired
	private DictDataService dictDataService;
	@Autowired
	private BillNoRecordService billNoRecordService;


	/**
	 * 获取单条数据
	 *
	 * @param billNoPattern
	 * @return
	 */
	@Override
	public BillNoPattern get(BillNoPattern billNoPattern) {
		return super.get(billNoPattern);
	}

	/**
	 * 查询分页数据
	 *
	 * @param billNoPattern 查询条件
	 * @param billNoPattern page 分页对象
	 * @return
	 */
	@Override
	public Page<BillNoPattern> findPage(BillNoPattern billNoPattern) {
		return super.findPage(billNoPattern);
	}

	/**
	 * 查询列表数据
	 *
	 * @param billNoPattern
	 * @return
	 */
	@Override
	public List<BillNoPattern> findList(BillNoPattern billNoPattern) {
		return super.findList(billNoPattern);
	}

	/**
	 * 保存数据（插入或更新）
	 *
	 * @param billNoPattern
	 */
	@Override
	@Transactional
	public void save(BillNoPattern billNoPattern) {
		super.save(billNoPattern);
	}

	/**
	 * 更新状态
	 *
	 * @param billNoPattern
	 */
	@Override
	@Transactional
	public void updateStatus(BillNoPattern billNoPattern) {
		super.updateStatus(billNoPattern);
	}

	/**
	 * 删除数据
	 *
	 * @param billNoPattern
	 */
	@Override
	@Transactional
	public void delete(BillNoPattern billNoPattern) {
		super.delete(billNoPattern);
	}


	// ----------------------------------------------------------------
	// 生成单号
	// ----------------------------------------------------------------

	/**
	 * 生成单号
	 */
	@Transactional
	public String generateBillNo(long orgId, String orgCode, String formCode, String paramStr) {
		long autoId = formService.getFormId(formCode);

		return generateBillNo(orgId, orgCode, BillPatternEnum.FORM, null, autoId, paramStr);
	}

	/**
	 * 生成多个单号
	 */
	@Transactional
	public List<String> generateBillNos(long orgId, String orgCode, String formSn, String paramStr, int genSize) {
		long autoId = formService.getFormId(formSn);

		return generateBillNos(orgId, orgCode, BillPatternEnum.FORM, null, autoId, paramStr, genSize);
	}

	// ----------------------------------------------------------------
	// 生成条码号
	// ----------------------------------------------------------------

	/**
	 * 生成条码号
	 */
	@Transactional
	public String generateBarcode(long orgId, String orgCode, BarcodeTypeEnum barcodeTypeEnum, String paramStr) {
		return generateBillNo(orgId, orgCode, BillPatternEnum.BARCODE, barcodeTypeEnum, Long.parseLong(barcodeTypeEnum.getCode()), paramStr);
	}

	/**
	 * 生成多个单号
	 */
	@Transactional
	public List<String> generateBarcodes(long orgId, String orgCode, BarcodeTypeEnum barcodeTypeEnum, String paramStr, int genSize) {
		return generateBillNos(orgId, orgCode, BillPatternEnum.BARCODE, barcodeTypeEnum, Long.parseLong(barcodeTypeEnum.getCode()), paramStr, genSize);
	}

	// ----------------------------------------------------------------
	// 单号/条码号 公用方法
	// ----------------------------------------------------------------

	/**
	 * 生成单号/条码号
	 */
	private String generateBillNo(long orgId, String orgCode, BillPatternEnum billPatternEnum, BarcodeTypeEnum barcodeTypeEnum, long sourceId, String paramStr) {
		Kv patternKv = getBillNoPatternKv(orgId, billPatternEnum.getSn(), sourceId, ObjUtil.isNull(barcodeTypeEnum) ? null : barcodeTypeEnum.getCode(), paramStr);

		String pattern = patternKv.getStr("pattern");

		Kv result = Kv.create();

		// 屏蔽同一类型的单号，并发生成
		synchronized ((orgId + billPatternEnum.getValue() + sourceId + pattern).intern()) {
			BillNoPattern billNoPattern = getBillNoPattern(orgId, orgCode, billPatternEnum, patternKv, pattern, sourceId);

			result.set("billNo", generateBillNo(billNoPattern));
		}

		// 生成单号记录
		return result.getStr("billNo");
	}

	private Kv getBillNoPatternKv(long orgId, String billTypeSn, long sourceId, String barcodeType, String paramStr) {
		// 查询到当前生成单号的编码配置
		CodingRuleHeader codingRuleHeader = codingRuleHeaderService.getCodingRuleHeader(orgId, billTypeSn, barcodeType, sourceId);
		ValidationUtils.notNull(codingRuleHeader, "缺少表单编码规则");
		ValidationUtils.equals(CodingTypeEnum.AUTOGEN.getCode(), codingRuleHeader.getCodingType(), "手工编码规则，不支持自动生成编码");

		// 获取编码规则集合
		List<CodingRuleLines> codingRuleLinesList = codingRuleHeaderService.getListOrderBySeq(codingRuleHeader.getAutoId());
		ValidationUtils.notEmpty(codingRuleLinesList, "编码规则明细不能为空");

		// 单号格式
		StringBuilder patternBuilder = new StringBuilder();

		// 分隔符字典
		DictData para = new DictData();
		para.setDictType("separator");
		List<DictData> list = dictDataService.findList(para);
		Map<String, String> separatorMap = new HashMap<>();
		for (DictData dictData : list) {
			separatorMap.put(dictData.getDictValue(), dictData.getDictLabel());
		}

		// 起始序号
		Integer initSeq = null;
		// 流水号长度
		Integer length = null;

		LocalDateTime date = LocalDateTime.now();

		for (CodingRuleLines codingRuleLines : codingRuleLinesList) {

			CodingLineTypeEnum codingTypeEnum = CodingLineTypeEnum.toEnum(codingRuleLines.getCodingType());

			// 编码字段类型
			switch (codingTypeEnum) {
				// 手工输入
				case inputType:
					patternBuilder.append(codingRuleLines.getCodingValue());
					break;

				// 流水号
				case serialNumberType:
					patternBuilder.append("${serial}");
					// 起始流水号
					initSeq = Integer.parseInt(codingRuleLines.getBeginValue());
					// 流水号长度
					length = codingRuleLines.getLength();
					break;

				case yearType:
				case year2Type:
				case monthType:
				case dateType:
					patternBuilder.append(date.format(DateTimeFormatter.ofPattern(codingTypeEnum.getDesc())));
					break;
				default:
					throw new ParameterException("未知编码字段类型");
			}

			// 存在分隔符
			if (StrUtil.isNotBlank(codingRuleLines.getSeparator())) {
				patternBuilder.append(separatorMap.get(codingRuleLines.getSeparator()));
			}
		}

		if (ObjUtil.isNull(initSeq) || ObjUtil.isNull(length)) {
			throw new ParameterException("自动编码要求存在一个流水号字段");
		}


		return Kv.by("initSeq", initSeq).set("length", length).set("pattern", patternBuilder.toString());
	}

	private List<String> generateBillNos(long orgId, String orgCode, BillPatternEnum billPatternEnum, BarcodeTypeEnum barcodeTypeEnum, long sourceId, String paramStr, int genSize) {
		Kv patternKv = getBillNoPatternKv(orgId, billPatternEnum.getSn(), sourceId, ObjUtil.isNull(barcodeTypeEnum) ? null : barcodeTypeEnum.getCode(), paramStr);

		String pattern = patternKv.getStr("pattern");

		Kv result = Kv.create();

		// 屏蔽同一类型的单号，并发生成
		synchronized ((orgId + billPatternEnum.getValue() + sourceId + pattern).intern()) {
			BillNoPattern billNoPattern = getBillNoPattern(orgId, orgCode, billPatternEnum, patternKv, pattern, sourceId);

			result.set("billNos", generateBillNos(billNoPattern, genSize));
		}
		return result.getAs("billNos");
	}

	private BillNoPattern save(long orgId, String orgCode, String type, long sourceId, String pattern, int serialLength, int initSeq, int seq) {
		BillNoPattern billNoPattern = new BillNoPattern();
		billNoPattern.setOrgId(orgId);
		billNoPattern.setOrgCode(orgCode);
		billNoPattern.setType(type);
		billNoPattern.setSourceId(sourceId);
		billNoPattern.setPattern(pattern);
		billNoPattern.setSerialLength(serialLength);
		billNoPattern.setInitSeq(initSeq);
		billNoPattern.setSeq(seq);
		billNoPattern.setVersionNum(0L);
		dao.insert(billNoPattern);
		return billNoPattern;
	}

	private BillNoPattern getBillNoPattern(long orgId, String orgCode, BillPatternEnum billPatternEnum, Kv patternKv, String pattern, long sourceId) {
		int length = ObjUtil.defaultIfNull(patternKv.getInt("length"), 0); // Default to 0 if null
		int initSeq = ObjUtil.defaultIfNull(patternKv.getInt("initSeq"), 0); // Default to 0 if null

		// 获取生成单号格式记录
		BillNoPattern billNoPattern = getPattern(orgId, billPatternEnum.getValue(), sourceId, pattern);

		if (ObjUtil.isNull(billNoPattern)) {
			billNoPattern = save(orgId, orgCode, billPatternEnum.getValue(), sourceId, pattern, length, initSeq, initSeq);
		}
		return billNoPattern;
	}

	private BillNoPattern getPattern(long orgId, String type, long sourceId, String pattern) {
		return dao.getPattern(orgId, type, sourceId, pattern);
	}

	/**
	 * 生成多个单号记录
	 *
	 * @param billNoPattern 单号格式记录
	 * @param genSize       生成个数
	 */
	private List<String> generateBillNos(BillNoPattern billNoPattern, int genSize) {
		// 指定位数，序号的最大整数值
		long max = (long) Math.pow(10, billNoPattern.getSerialLength()) - 1;
		ValidationUtils.isTrue(max - billNoPattern.getSeq() - genSize >= 0, "生成序号将溢出，请扩展序号位数");

		// 生成序号之后的最新序号值
		int newIseq = billNoPattern.getSeq();

		// 单号格式
		String pattern = billNoPattern.getPattern();

		List<String> billNos = new ArrayList<>();

		List<BillNoRecord> billNoRecords = new ArrayList<>();

		for (int i = 0; i < genSize; i++) {
			// 指定位数，前面不足时，补0
			String cSerial = StrUtil.fillBefore(String.valueOf(newIseq), '0', billNoPattern.getSerialLength());
			// 单号
			String billNo = TemplateUtil.createEngine().getTemplate(pattern).render(Okv.by("serial", cSerial));

			// 生成流水号
			billNoRecords.add(billNoRecordService.init(billNoPattern.getAutoId(), newIseq, cSerial, billNo));

			// 增加序号值
			newIseq++;

			billNos.add(billNo);
		}

		// 批量保存生成的条码记录
		billNoRecordService.insertBatch(billNoRecords);

		// 更新生成的序号
		ValidationUtils.isTrue(updateSeq(billNoPattern.getAutoId(), newIseq, billNoPattern.getSerialLength(), billNoPattern.getVersionNum()), "生成单号失败");

		return billNos;
	}

	/**
	 * 生成单个单号记录
	 */
	private String generateBillNo(BillNoPattern billNoPattern) {
		// 指定位数，序号的最大整数值
		long max = (long) Math.pow(10, billNoPattern.getSerialLength()) - 1;
		ValidationUtils.isTrue(max - billNoPattern.getSeq() >= 0, "生成序号溢出，请扩展序号位数");

		// 生成序号之后的最新序号值
		int newSeq = billNoPattern.getSeq();

		// 单号格式
		String pattern = billNoPattern.getPattern();

		// 指定位数，前面不足时，补0
		String cSerial = StrUtil.fillBefore(String.valueOf(newSeq), '0', billNoPattern.getSerialLength());
		// 单号
		String billNo = TemplateUtil.createEngine().getTemplate(pattern).render(Okv.by("serial", cSerial));

		// 生成流水号
		billNoRecordService.saveBillNoRecord(billNoPattern.getAutoId(), newSeq, cSerial, billNo);

		// 更新生成的序号
		ValidationUtils.isTrue(updateSeq(billNoPattern.getAutoId(), newSeq + 1, billNoPattern.getSerialLength(), billNoPattern.getVersionNum()), "生成单号失败");

		return billNo;
	}

	/**
	 * 更新当前流水号
	 */
	public boolean updateSeq(long autoId, int seq, int serialLength, long versionNum) {
		return dao.updateSeq(autoId, seq, serialLength, IdGen.autoId(), versionNum) > 0;
	}

}